/* Commenté
	Di-Costanzo Jonathan
	Fauché Pierre-Élie
	3TC 3
*/


/* Copyright (c) 2004 by Dept TC INSA Lyon.  All Rights Reserved */

#include <stdlib.h>
#include <search.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

#include <dirent.h>
#include <regex.h>

#include "p2p_common.h"
#include "p2p_file.h"
#include "p2p_msg.h"

#define DIR_SEPARATOR '/' // OK pour UNIX, à changer pour Windows par "\"

/***************************************************************************
structure pour stocker des infos sur un fichier
***************************************************************************/
struct p2p_files_item_struct {
	char name[MAX_PATH];
	unsigned long int size;
};

/***************************************************************************
structure pour stocker les fichiers trouvés sur le serveur
***************************************************************************/
struct p2p_files_struct {
	int length;
	p2p_files_item files;
};


/* utilisation de p2p_files */

/***************************************************************************
Création (allocation) d'un p2p_files
***************************************************************************/
p2p_files p2p_files_create() {
	p2p_files retour;
	
	retour = (p2p_files)malloc(sizeof(struct p2p_files_struct));
	
	retour->length=0;
	retour->files=NULL;
	
	return retour;
}

/***************************************************************************
Suppression de la liste des fichiers
***************************************************************************/
void p2p_files_delete(p2p_files liste) {
	free(liste->files);
	free(liste);	
}

/***************************************************************************
Ajout d'un élément à la liste des fichiers
***************************************************************************/
void p2p_files_add(p2p_files liste, const char* nom_fic, const unsigned long int taille) {
	
	liste->files=(p2p_files_item)realloc(liste->files, 
		(liste->length+1)*sizeof(struct p2p_files_item_struct));
	
	strcpy(liste->files[liste->length].name, nom_fic);
	liste->files[liste->length].size   = taille;
	
	liste->length++;
}

/***************************************************************************
Récupère l'attribut "length" d'une structure p2p_files_struct
***************************************************************************/
int p2p_files_length(p2p_files liste) {
	return (liste->length);
}

/***************************************************************************
Récupère le nom du fichier.
Retourne null en cas d'erreur
***************************************************************************/
char* p2p_files_get_name(p2p_files liste, const int offset) {
	
	if ( liste->length > offset ) {
		return (liste->files[offset].name);
	}
	
	return NULL;
}

/***************************************************************************
Récupère la taille du fichier.
Retourne -1 en cas d'erreur
***************************************************************************/
unsigned long int p2p_files_get_size(p2p_files liste, const int offset) {
	
	if ( liste->length > offset ) {
		return (liste->files[offset].size);
	}
	return -1;
}



/***************************************************************************
	Obtenir le chemin absolu vers un fichier spécifié
***************************************************************************/

// sp : paramètres du serveur (répertoire utilisé)
// file : nom du fichier à manipuler
// retourne le chemin absolu du fichier (répertoire + fichier)
char* complete_path(server_params*sp, const char* file)
{
  char* ptr;
  char* fullpath;
  if ((ptr = strrchr(file,DIR_SEPARATOR)) == NULL)
    {
      ptr = (char*)file;
    }

  fullpath = (char*)malloc(sizeof(char)*(strlen(sp->dir_name)+1+strlen(ptr)+1));
  sprintf(fullpath,"%s/%s",sp->dir_name,ptr);
  return fullpath;
}

/***************************************************************************
	vérifie si un fichier spécifié est disponible
***************************************************************************/

// sp : paramètres du serveur (nom du répertoire...)
// file : nom du fichier dont on vérifie la présence
// filesize : taille du fichier recherché ou code d'erreur
// retourne P2P_OK ou P2P_ERROR
int
p2p_file_is_available(server_params *sp, const char* file, unsigned long int *filesize) {
  int fd; // descripteur de fichier
  int ret = P2P_OK; // code retour, OK ou ERROR
  char* fullpath; // chemin absolu vers le fichier
  struct stat buf;

  *filesize = 0;
  fullpath = complete_path(sp,file); // on prend le chemin absolu 
  VERBOSE(sp,VSYSCL,"** p2p_file lookup for %s\n",fullpath);
  fd = open(fullpath, O_RDONLY); // on tente d'ouvrir le fichier demandé
  if (fd == -1) { // on a pas réussi à lire le fichier...
    ret = P2P_ERROR; // ... donc on retourne ERROR
    switch (errno){ // errno contient le code erreur de open
      case EACCES:
		*filesize = P2P_DATA_FORBIDDEN;
		break;
      case ENAMETOOLONG:
		*filesize = P2P_REQUEST_URI_TOO_LARGE;
		break;
      case ENOENT:
		*filesize = P2P_DATA_NOT_FOUND;
		break;
      case ENOTDIR:
		*filesize = P2P_BAD_REQUEST;
		break;
      default:
		*filesize = P2P_INTERNAL_SERVER_ERROR;
		break;
  	}
	} else { // on a réussi à ouvrir le fichier en lecture
    if ( stat(fullpath, &buf) ) { // stat renvoie des infos sur le fichier indiqué
	  ret = P2P_ERROR; // si stat retourne -1, ça n'a pas réussi donc ERROR
	  switch (errno){ // errno contient le code erreur
	  	case EACCES:
	      *filesize = P2P_DATA_FORBIDDEN;
	      break;
	    case ENAMETOOLONG:
	      *filesize = P2P_REQUEST_URI_TOO_LARGE;
	      break;
	    case ENOENT:
	      *filesize = P2P_DATA_NOT_FOUND;
	      break;
	    case ENOTDIR:
	      *filesize = P2P_BAD_REQUEST;
	      break;
	    default:
	      *filesize = P2P_INTERNAL_SERVER_ERROR;
	      break;
	  } // switch
	} else {
			// si tout a réussi, on récupère la taille du fichier
	  *filesize = buf.st_size;
	}
    close(fd);
  }
  free(fullpath);
  return ret;
} 

/***************************************************************************
	obtenir une partie d'un fichier (dont on a spécifié l'offset)
***************************************************************************/

// sp : paramètres du serveur (nom du répertoire...)
// file : nom du fichier à lire
// boffset : début de l'offset
// eoffset : fin de l'offset
// data : pour stocker les données que l'on va lire
// retourne P2P_OK ou P2P_ERROR
int
p2p_file_get_chunck(server_params* sp, const char* file, int boffset, int eoffset, unsigned char** data) {
  int size; // taille de la partie à récupérer
  FILE* fd; // descripteur de fichier
  char* fullpath; // chemin absolu
  
  fullpath = complete_path(sp,file); // récupération du chemin absolu
  size = eoffset - boffset + 1; // calcul de la taille de la partie
  VERBOSE(sp,VSYSCL,"%d bytes requested start:%d end:%d\n",size,boffset,eoffset);
  if ( (*data = (unsigned char*)malloc(size)) == NULL) { // allocation de la place nécessaire
    return P2P_ERROR; // si l'allocation n'a pas réussi
  }
  
  VERBOSE(sp,VSYSCL,"memory allocation ok\n");
  if ((fd = fopen(fullpath,"r")) == NULL) { // ouverture du fichier en lecture
  	// si l'ouverture a échoué
    free(*data); // on libère la place allouée
    *data = NULL;
    return P2P_ERROR;
  }
  
  VERBOSE(sp,VSYSCL,"fopen ok\n");
  fseek(fd,boffset,SEEK_SET); // on se place au début de l'offset
  fread(*data,1,size,fd); // on lit le fichier et on stocke dans data
  fclose(fd);
  free(fullpath);
  fprintf(stderr, ">>> get shunk fini\n");
  return P2P_OK; // tout a fonctionné
}

/***************************************************************************
crée un fichier dont on donne le nom et la taille et le remplir de 'x'
***************************************************************************/

// sp : paramètres du serveur (nom du répertoire...)
// file : nom du fichier à créer
// size : taille du fichier à créer
// retourne P2P_OK ou P2P_ERROR
int            
p2p_file_create_file(server_params* sp, const char* file, int size) {
  unsigned char* buf;
  FILE* fd; // descripteur de fichier
  char* fullpath;
  
  fullpath = complete_path(sp,file);
  if ( (fd = fopen(fullpath,"w")) == NULL ) { // ouverture du fichier en écriture
  	// si l'ouverture ne fonctionne pas
    free(fullpath);
    return P2P_ERROR;
  }
  
  // on alloue à buf la taille du fichier et on le remplit de 'x'
  buf = (unsigned char*)malloc(size*sizeof(char));
  memset(buf,'x',size);
  fwrite(buf,1,size,fd); // on écrit le 'buf' dans le fichier
  fclose(fd);
  free(buf);
  free(fullpath);
  return P2P_OK; // tout à fonctionné
}

/***************************************************************************
à la réception du DATA, on écrit la partie du fichier réceptionnée
***************************************************************************/

// sp : paramètres du serveur (nom du répertoire...)
// file : nom du fichier à écrire
// boffset : début de l'offset
// eoffset : fin de l'offset
// data : stocke les données que l'on va écrire dans le fichier
// retourne P2P_OK ou P2P_ERROR
int            
p2p_file_set_chunck(server_params* sp, const char* file, int boffset, int eoffset, unsigned char* data) {
  FILE* fd;
  char* fullpath;
  
  fullpath = complete_path(sp,file);
  if ((fd = fopen(fullpath,"r+")) == NULL) { // ouverture en lecture/écriture
  	// l'ouverture n'a pas fonctionné
    free(fullpath);
    return P2P_ERROR;
  }
  
  fseek(fd,boffset,SEEK_SET); // on se place au bon offset
  fwrite(data,1,eoffset - boffset + 1, fd); // on écrit le data
  fclose(fd);
  free(fullpath);
  return P2P_OK; // tout a fonctionné
}

/***************************************************************************
retourne la liste des fichiers disponibles
***************************************************************************/
p2p_files
p2p_files_available(server_params *sp, const char* file, const unsigned char version) {
	
	p2p_files liste=p2p_files_create();
	unsigned long int filesize;
	struct stat buf;
	char *fullpath;
  
  	struct dirent *lecture;
  	DIR *rep;
  	rep = opendir(sp->dir_name);
  	regex_t preg;
  	
  	char* ptr; // pointe sur le nom du fichier 
  	if ( (ptr = strrchr(file,DIR_SEPARATOR)) == NULL ) {
    	ptr = (char*)file;
  	}
  
  	if (regcomp (&preg, ptr, REG_ICASE | REG_NOSUB | REG_EXTENDED) == 0) {
    	while ((lecture = readdir(rep))) {
    		if (regexec (&preg, lecture->d_name, 0, NULL, 0)== 0){
        	//fichier correspondant à l'expression régulière
        	fullpath=complete_path(sp, lecture->d_name);
        	if ( stat(fullpath, &buf) ) { // stat renvoie des infos sur le fichier indiqué
	  				continue;
	  			} else {
						filesize = buf.st_size;
					}
        	p2p_files_add(liste, lecture->d_name, filesize);
        	free(fullpath);
        }
  		}
  		closedir(rep);
  		regfree (&preg);
		}
 
  
	return liste;
}

